package com.free.bsf.sentinel;

import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.Tracer;
import com.alibaba.csp.sentinel.context.ContextUtil;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRuleManager;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.slots.system.SystemRule;
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
import com.free.bsf.core.base.Callable;
import com.free.bsf.core.base.LimitRateException;
import com.free.bsf.core.util.JsonUtils;
import com.free.bsf.core.util.LogUtils;
import com.free.bsf.core.util.PropertyUtils;
import com.free.bsf.sentinel.base.SphEntry;
import lombok.val;
import lombok.var;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;

public class SentinelUtils {
    /**
     * 限流
     * @param sphEntry
     * @param action0
     */
    public static void run(SphEntry sphEntry, Callable.Action0 action0) {
        Entry entry = null;
        // 务必保证 finally 会被执行
        try {
            ContextUtil.enter(sphEntry.getName());
            // 资源名可使用任意有业务语义的字符串，注意数目不能太多（超过 1K），超出几千请作为参数传入而不要直接作为资源名
            // EntryType 代表流量类型（inbound/outbound），其中系统规则只对 IN 类型的埋点生效
            if(sphEntry.hasArgs()){
                entry = SphU.entry(sphEntry.getName(),sphEntry.getTrafficType(),sphEntry.getBatchCount(),sphEntry.getArgs());
            }else {
                entry = SphU.entry(sphEntry.getName());
            }
            // 被保护的业务逻辑
            action0.invoke();

        } catch (BlockException ex) {
            // 资源访问阻止，被限流或被降级
            // 进行相应的处理操作
            throw new LimitRateException("请求流量被限制或被降级",ex);
        } catch (Exception ex) {
            // 若需要配置降级规则，需要通过这种方式记录业务异常
            Tracer.traceEntry(ex, entry);
            throw ex;
        } finally {
            if(sphEntry.hasArgs()) {
                if (entry != null) {
                    entry.exit(sphEntry.getBatchCount(),sphEntry.getArgs());
                }
            }else{
                // 务必保证 exit，务必保证每个 entry 与 exit 配对
                if (entry != null) {
                    entry.exit();
                }
            }
            ContextUtil.exit();
        }
    }

    protected static List<String> getConfigRuleKeys(){
        List<String> rs = new ArrayList<>();
        PropertyUtils.eachProperty2((k,v)->{
            if(!k.startsWith(SentinelProperties.BsfSentinel)){
                return;
            }
            rs.add(k);
        });
        return rs;
    }

    /**
     * 限流配置规则解析加载
     * @param ruleName
     * @param configRuleKeys
     */
    public static void loadRule(String ruleName,List<String> configRuleKeys){
        List<String> rs = new ArrayList<>();
        for(val r:configRuleKeys){
            if(r.contains(ruleName)){
                val value = PropertyUtils.getPropertyCache(r,"");
                if(!StringUtils.isEmpty(value))
                {rs.add(value);}
            }
        }
        if("FlowRule".equalsIgnoreCase(ruleName)) {
            List<FlowRule> rules = new ArrayList<>();
            for (val r : rs) {
                try {
                    FlowRule o = JsonUtils.deserialize(r, FlowRule.class);
                    o.setResource(o.getResource());
                    rules.add(o);
                } catch (Exception e) {
                    LogUtils.error(SentinelUtils.class, SentinelProperties.Project, "限流规则解析失败,自动跳过:" + r);
                }
            }
            FlowRuleManager.loadRules(rules);
            return;
        }
        if("DegradeRule".equalsIgnoreCase(ruleName)) {
            List<DegradeRule> rules = new ArrayList<>();
            for (val r : rs) {
                try {
                    DegradeRule o = JsonUtils.deserialize(r, DegradeRule.class);
                    o.setResource(o.getResource());
                    rules.add(o);
                } catch (Exception e) {
                    LogUtils.error(SentinelUtils.class, SentinelProperties.Project, "限流规则解析失败,自动跳过:" + r);
                }
            }
            DegradeRuleManager.loadRules(rules);
            return;
        }
        if("SystemRule".equalsIgnoreCase(ruleName)) {
            List<SystemRule> rules = new ArrayList<>();
            for (val r :rs) {
                try {
                    rules.add(JsonUtils.deserialize(r, SystemRule.class));
                } catch (Exception e) {
                    LogUtils.error(SentinelUtils.class, SentinelProperties.Project, "限流规则解析失败,自动跳过:" + r);
                }
            }
            SystemRuleManager.loadRules(rules);
            return;
        }
        if("AuthorityRule".equalsIgnoreCase(ruleName)) {
            List<AuthorityRule> rules = new ArrayList<>();
            for (val r : rs) {
                try {
                    AuthorityRule o = JsonUtils.deserialize(r, AuthorityRule.class);
                    o.setResource(o.getResource());
                    rules.add(o);
                } catch (Exception e) {
                    LogUtils.error(SentinelUtils.class, SentinelProperties.Project, "限流规则解析失败,自动跳过:" + r);
                }
            }
            AuthorityRuleManager.loadRules(rules);
            return;
        }
//        if("ParamRule".equalsIgnoreCase(ruleName)) {
//            List<ParamFlowRule> rules = new ArrayList<>();
//            for (val r : rs) {
//                try {
//                    rules.add(JsonUtils.deserialize(r, ParamFlowRule.class));
//                } catch (Exception e) {
//                    LogUtils.error(SentinelUtils.class, SentinelProperties.Project, "限流规则解析失败,自动跳过:" + r);
//                }
//            }
//            ParamFlowRuleManager.loadRules(rules);
//            return;
//        }
    }

    /**
     * 是否匹配include规则
     * @param source
     * @param rules
     * @return
     */
    public static boolean isLikeMatch(String source,String[] rules){
        for(val r:rules){
            if(isLikeMatch(source,r)){
                return true;
            }
        }
        return false;
    }

    /**
     * 前后*号做模糊匹配
     * @param source
     * @param rule
     * @return
     */
    private static boolean isLikeMatch(String source,String rule){
        if(StringUtils.isEmpty(rule)){
            return false;
        }
        if("*".equals(rule)){
            return true;
        }
        if(source==null)
            return false;
        if(rule.startsWith("*")&&rule.endsWith("*")){
            var str = StringUtils.trimLeadingCharacter(rule,'*');
            str = StringUtils.trimTrailingCharacter(rule,'*');
            return source.contains(str);
        }
        if(rule.startsWith("*")){
            var str = StringUtils.trimLeadingCharacter(rule,'*');
            return source.endsWith(str);
        }
        if(rule.endsWith("*")){
            var str = StringUtils.trimTrailingCharacter(rule,'*');
            return source.startsWith(str);
        }
        return source.equals(rule);
    }

    public static void refreshConfig(List<String> configRefresh,boolean refreshAll){
        val configRules = SentinelUtils.getConfigRuleKeys();
        if(refreshAll||configRefresh.stream().anyMatch(c->c.startsWith(SentinelProperties.BsfSentinelFlowRule))) {
            SentinelUtils.loadRule("flowRule", configRules);
        }
        if(refreshAll||configRefresh.stream().anyMatch(c->c.startsWith(SentinelProperties.BsfSentinelDegradeRule))) {
            SentinelUtils.loadRule("degradeRule", configRules);
        }
        if(refreshAll||configRefresh.stream().anyMatch(c->c.startsWith(SentinelProperties.BsfSentinelSystemRule))) {
            SentinelUtils.loadRule("systemRule", configRules);
        }
        if(refreshAll||configRefresh.stream().anyMatch(c->c.startsWith(SentinelProperties.BsfSentinelAuthorityRule))) {
            SentinelUtils.loadRule("authorityRule", configRules);
        }
        if(refreshAll||configRefresh.stream().anyMatch(c->c.startsWith(SentinelProperties.BsfSentinelParamRule))) {
            SentinelUtils.loadRule( "paramRule", configRules);
        }
    }
}
